Example pair
D_T8_C1R2_1 D_T8_C1R2_2 D_T8_C1R2_5-95_1_2
list_images <- read_csv("~/Desktop/Lab/emergent-coexistence/output/check/00-list_images-D.csv")
Rows: 208 Columns: 22
── Column specification ─────────────────────────────────────────────────────────────
Delimiter: ","
chr (22): image_name, folder_original, folder_green, folder_green_rolled, folder_...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
0. original image
image_original <- readImage(paste0(list_images$folder_original[i], image_name, ".tiff"))
writeImage(image_original, paste0(folder_main, "example/", image_name, "-00-original.tiff"))
display(image_original, method = "raster")

1. Green channel
temp <- image_original
colorMode(temp) = Grayscale
image_green <- temp[,,2]
writeImage(image_green, paste0(folder_main, "example/", image_name, "-01-green.tiff"))
display(image_green, method = "raster")

2. Rolling ball
Python code from rolling_ball.py
py$file_green <- paste0(list_images$folder_green[i], image_name, ".tiff")
py$file_green_rolled <- paste0(list_images$folder_green_rolled[i], image_name, "-02-rolled.tiff")
The R variables should be passed to python but somehow failed. Not
sure where the problem is from, probably reticualte. 20220902 use the
externally generated rolled images.
import imageio
import os
import sys
import skimage
from skimage import data, restoration, util, io, color
def rolling_ball_light(image):
# 2. invert the image
image_inverted = util.invert(image)
# 3. rolling ball
background_inverted = restoration.rolling_ball(image_inverted, radius = 80, num_threads = 10)
# 4. invert the result
image_rolled_inverted = image_inverted - background_inverted
image_rolled = util.invert(image_rolled_inverted)
return image_rolled
image = io.imread(file_green)
image_rolled = rolling_ball_light(image)
io.imsave(file_green_rolled, image_rolled)
Display the rolled result
image_rolled <- readImage(paste0(list_images$folder_green_rolled[i], image_name, ".tiff"))
writeImage(image_rolled, paste0(folder_main, "example/", image_name, "-02-green_rolled.tiff"))
display(image_rolled, method = "raster")

3. Thresholding
Here because the images have undergone gray scale and background
subtraction so I apply a global threshold to the image
threshold <- otsu(image_rolled)
image_thresholded <- image_rolled < threshold
writeImage(image_thresholded, paste0(folder_main, "example/", image_name, "-03-threshold.tiff"))
display(image_thresholded, method = "raster")

4. Detect round shaped object and remove super small size
To select potential colonies: area, perimeter, and circularity.
This step is implemented here to prevent over segmentation and reduce
segmentation load
As you can see here, attached objects are not divided yet
Filter objects by size
image_object <- bwlabel(image_thresholded)
object_ID_nonround <- detect_nonround_object(image_object, watershed = F)
image_round <- rmObjects(image_object, object_ID_nonround, reenumerate = T)
writeImage(image_round, paste0(folder_main, "example/", image_name, "-04-size_objects.tiff"))
display(image_round, method = "raster")

5. Distance map
The distance map contains for each pixel the distance to the nearest
background pixe
image_distancemap <- distmap(image_round)
writeImage(normalize(image_distancemap), paste0(folder_main, "example/", image_name, "-05-distancemap.tiff"))
display(normalize(image_distancemap), method = "raster")

6. Watershed
This is the actual segmentation step. Here the adjacent objects are
better distinguished

7. Features
image_watershed <- watershed(image_distancemap, tolerance = 1)
display(colorLabels(image_watershed), method = "raster")
# 7. Calculate the features
library(EBImageExtra)
library(purrr)
extract_transection <- function (watershed, ref) {
#' This function searches for all objects on an image and return the pixel intensity along the transection of each object
#' Arguments:
#' watershed is the watershed image with object labels
#' ref is the original image with intensity data. This has to be single-channel
oc <- ocontour(watershed) # all contour points
transection <- NULL
#max_radius <- NULL # A test for object ID -> good. As long I use the oc output, they should be good
for (j in 1:length(oc)) {
z <- oc[[j]] # coordinate of contour pixels
cz <- apply(z, 2, mean) # object center
radius <- sqrt(rowSums((z - rep(cz, each=nrow(z)))^2)) # all radius from the center pixel to the contour pixels
mz <- z[which.min(abs(radius - median(radius))),] # the coordinate of contour pixels with the median radius (or closest to the median)
# The coordinates of all along the line to the maximum line: this is arranged such that it's always from the center to the periphery
lz <- bresenham(c(cz[1], mz[1]), c(cz[2], mz[2]))
radial_gradient <- NULL
for (k in 1:length(lz$x)) radial_gradient[k] <- ref@.Data[lz$x[k], lz$y[k]]
transection[[j]] <- tibble(x = lz$x, y = lz$y, Intensity = radial_gradient)
}
return(transection)
}
draw_pixels <- function (img, pixel.x, pixel.y) {
#' This function takes an image and draw red on the assigned pixels
stopifnot(length(pixel.x) == length(pixel.y))
# Render the gray-scale image back into color mode
img <- abind(img, img, img, along = 3) %>% Image(colormode = "Color")
# Create a logical mask
m <- Image(T, dim(img)[1:2])
for (i in 1:length(pixel.x)) m[pixel.x[i], pixel.y[i]] <- F
M <- abind(m, m, m, along = 3)
# Combine with solid colored image, replace appropriate pixels in image
mask <- Image("red", dim = dim(img)[1:2], colormode = "Color")
ans <- img * M + mask * !M
return(ans)
}

Plot two pairs’s transection for example
Output the example
# # writeImage(image_original, paste0(folder_main, "example/", image_name, "-00-original.tiff"))
# # writeImage(image_green, paste0(folder_main, "example/", image_name, "-01-green.tiff"))
# writeImage(image_rolled, paste0(folder_main, "example/", image_name, "-02-rolled.tiff"))
# writeImage(image_thresholded, paste0(folder_main, "example/", image_name, "-03-threshold.tiff"))
# #writeImage(colorLabels(image_object), paste0(folder_main, "example/", image_name, "-03a-object.tiff"))
# writeImage(image_round, paste0(folder_main, "example/", image_name, "-04-round_object.tiff"))
# writeImage(normalize(image_distancemap), paste0(folder_main, "example/", image_name, "-05-distance_map.tiff"))
# writeImage(colorLabels(image_watershed), paste0(folder_main, "example/", image_name, "-06-watershed.tiff"))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiNrbml0cjo6b3B0c19jaHVuaygpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KEVCSW1hZ2UpCmxpYnJhcnkocmV0aWN1bGF0ZSkKZm9sZGVyX21haW4gPC0gIi9Vc2Vycy9jaGFuZy15dS9Ecm9wYm94L2xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9kYXRhL3Jhdy9wbGF0ZV9zY2FuL2VtZXJnZW50X2NvZXhpc3RlbmNlX3BsYXRlX3NjYW5fY2hlY2svIgpgYGAKCgpFeGFtcGxlIHBhaXIgCgpEX1Q4X0MxUjJfMQpEX1Q4X0MxUjJfMiAKRF9UOF9DMVIyXzUtOTVfMV8yCgoKYGBge3J9CiMgbGlzdF9pbWFnZXMgPC0gdGliYmxlKAojICAgICBpbWFnZV9uYW1lID0gYygiRF9UOF9DMVIyXzEiLCAiRF9UOF9DMVIyXzIiLCAiRF9UOF9DMVIyXzUtOTVfMV8yIiksCiMgICAgIGZvbGRlcl9vcmlnaW5hbCA9IHJlcChwYXN0ZTAoZm9sZGVyX21haW4sICJjaGVjay9ELTAwLW9yaWdpbmFsLyIpLCAzKSwKIyAgICAgZm9sZGVyX2dyZWVuID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpLAojICAgICBmb2xkZXJfZ3JlZW5fcm9sbGVkID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpLAojICAgICBmb2xkZXJfZ3JlZW5fd2F0ZXJzaGVkID0gcmVwKHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiksIDMpLAojICAgICBmb2xkZXJfZ3JlZW5fZmVhdHVyZSA9IHJlcChwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIpLCAzKSwKIyAgICAgZm9sZGVyX2dyZWVuX2NsdXN0ZXIgPSByZXAocGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iKSwgMykKIyApCgpsaXN0X2ltYWdlcyA8LSByZWFkX2Nzdigifi9EZXNrdG9wL0xhYi9lbWVyZ2VudC1jb2V4aXN0ZW5jZS9vdXRwdXQvY2hlY2svMDAtbGlzdF9pbWFnZXMtRC5jc3YiKQoKaSA9IHdoaWNoKGxpc3RfaW1hZ2VzJGltYWdlX25hbWUgPT0gIkRfVDhfQzFSMl8xIikKaW1hZ2VfbmFtZSA8LSBsaXN0X2ltYWdlcyRpbWFnZV9uYW1lW2ldCgpgYGAKCiMgMC4gb3JpZ2luYWwgaW1hZ2UKCmBgYHtyfQppbWFnZV9vcmlnaW5hbCA8LSByZWFkSW1hZ2UocGFzdGUwKGxpc3RfaW1hZ2VzJGZvbGRlcl9vcmlnaW5hbFtpXSwgaW1hZ2VfbmFtZSwgIi50aWZmIikpCndyaXRlSW1hZ2UoaW1hZ2Vfb3JpZ2luYWwsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMC1vcmlnaW5hbC50aWZmIikpCmRpc3BsYXkoaW1hZ2Vfb3JpZ2luYWwsIG1ldGhvZCA9ICJyYXN0ZXIiKQpgYGAKCiMgMS4gR3JlZW4gY2hhbm5lbAoKYGBge3J9CnRlbXAgPC0gaW1hZ2Vfb3JpZ2luYWwKY29sb3JNb2RlKHRlbXApID0gR3JheXNjYWxlCmltYWdlX2dyZWVuIDwtIHRlbXBbLCwyXQp3cml0ZUltYWdlKGltYWdlX2dyZWVuLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDEtZ3JlZW4udGlmZiIpKQpkaXNwbGF5KGltYWdlX2dyZWVuLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKCiMgMi4gUm9sbGluZyBiYWxsCgpQeXRob24gY29kZSBmcm9tIGByb2xsaW5nX2JhbGwucHlgCgpgYGB7cn0KcHkkZmlsZV9ncmVlbiA8LSBwYXN0ZTAobGlzdF9pbWFnZXMkZm9sZGVyX2dyZWVuW2ldLCBpbWFnZV9uYW1lLCAiLnRpZmYiKQpweSRmaWxlX2dyZWVuX3JvbGxlZCA8LSBwYXN0ZTAobGlzdF9pbWFnZXMkZm9sZGVyX2dyZWVuX3JvbGxlZFtpXSwgaW1hZ2VfbmFtZSwgIi0wMi1yb2xsZWQudGlmZiIpCmBgYAoKVGhlIFIgdmFyaWFibGVzIHNob3VsZCBiZSBwYXNzZWQgdG8gcHl0aG9uIGJ1dCBzb21laG93IGZhaWxlZC4gTm90IHN1cmUgd2hlcmUgdGhlIHByb2JsZW0gaXMgZnJvbSwgcHJvYmFibHkgcmV0aWN1YWx0ZS4gMjAyMjA5MDIgdXNlIHRoZSBleHRlcm5hbGx5IGdlbmVyYXRlZCByb2xsZWQgaW1hZ2VzLgoKYGBge3B5dGhvbiBldmFsID0gRn0KaW1wb3J0IGltYWdlaW8KaW1wb3J0IG9zCmltcG9ydCBzeXMKaW1wb3J0IHNraW1hZ2UKZnJvbSBza2ltYWdlIGltcG9ydCBkYXRhLCByZXN0b3JhdGlvbiwgdXRpbCwgaW8sIGNvbG9yCgpkZWYgcm9sbGluZ19iYWxsX2xpZ2h0KGltYWdlKToKICAgICMgMi4gaW52ZXJ0IHRoZSBpbWFnZQogICAgaW1hZ2VfaW52ZXJ0ZWQgPSB1dGlsLmludmVydChpbWFnZSkKICAgIAogICAgIyAzLiByb2xsaW5nIGJhbGwKICAgIGJhY2tncm91bmRfaW52ZXJ0ZWQgPSByZXN0b3JhdGlvbi5yb2xsaW5nX2JhbGwoaW1hZ2VfaW52ZXJ0ZWQsIHJhZGl1cyA9IDgwLCBudW1fdGhyZWFkcyA9IDEwKQogICAgCiAgICAjIDQuIGludmVydCB0aGUgcmVzdWx0CiAgICBpbWFnZV9yb2xsZWRfaW52ZXJ0ZWQgPSBpbWFnZV9pbnZlcnRlZCAtIGJhY2tncm91bmRfaW52ZXJ0ZWQKICAgIGltYWdlX3JvbGxlZCA9IHV0aWwuaW52ZXJ0KGltYWdlX3JvbGxlZF9pbnZlcnRlZCkKICAgIHJldHVybiBpbWFnZV9yb2xsZWQKICAgIAppbWFnZSA9IGlvLmltcmVhZChmaWxlX2dyZWVuKQppbWFnZV9yb2xsZWQgPSByb2xsaW5nX2JhbGxfbGlnaHQoaW1hZ2UpCmlvLmltc2F2ZShmaWxlX2dyZWVuX3JvbGxlZCwgaW1hZ2Vfcm9sbGVkKQpgYGAKCkRpc3BsYXkgdGhlIHJvbGxlZCByZXN1bHQKCmBgYHtyfQppbWFnZV9yb2xsZWQgPC0gcmVhZEltYWdlKHBhc3RlMChsaXN0X2ltYWdlcyRmb2xkZXJfZ3JlZW5fcm9sbGVkW2ldLCBpbWFnZV9uYW1lLCAiLnRpZmYiKSkKd3JpdGVJbWFnZShpbWFnZV9yb2xsZWQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMi1ncmVlbl9yb2xsZWQudGlmZiIpKQpkaXNwbGF5KGltYWdlX3JvbGxlZCwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKCgojIDMuIFRocmVzaG9sZGluZwoKSGVyZSBiZWNhdXNlIHRoZSBpbWFnZXMgaGF2ZSB1bmRlcmdvbmUgZ3JheSBzY2FsZSBhbmQgYmFja2dyb3VuZCBzdWJ0cmFjdGlvbiBzbyBJIGFwcGx5IGEgZ2xvYmFsIHRocmVzaG9sZCB0byB0aGUgaW1hZ2UKCmBgYHtyfQp0aHJlc2hvbGQgPC0gb3RzdShpbWFnZV9yb2xsZWQpCmltYWdlX3RocmVzaG9sZGVkIDwtIGltYWdlX3JvbGxlZCA8IHRocmVzaG9sZAp3cml0ZUltYWdlKGltYWdlX3RocmVzaG9sZGVkLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDMtdGhyZXNob2xkLnRpZmYiKSkKZGlzcGxheShpbWFnZV90aHJlc2hvbGRlZCwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKIyA0LiBEZXRlY3Qgcm91bmQgc2hhcGVkIG9iamVjdCBhbmQgcmVtb3ZlIHN1cGVyIHNtYWxsIHNpemUKClRvIHNlbGVjdCBwb3RlbnRpYWwgY29sb25pZXM6IGFyZWEsIHBlcmltZXRlciwgYW5kIGNpcmN1bGFyaXR5LgoKVGhpcyBzdGVwIGlzIGltcGxlbWVudGVkIGhlcmUgdG8gcHJldmVudCBvdmVyIHNlZ21lbnRhdGlvbiBhbmQgcmVkdWNlIHNlZ21lbnRhdGlvbiBsb2FkCgpBcyB5b3UgY2FuIHNlZSBoZXJlLCBhdHRhY2hlZCBvYmplY3RzIGFyZSBub3QgZGl2aWRlZCB5ZXQKCmBgYHtyfQpkZXRlY3Rfbm9ucm91bmRfb2JqZWN0IDwtIGZ1bmN0aW9uIChpbWFnZV9vYmplY3QsIHdhdGVyc2hlZCA9IEYpIHsKICAgICMgUmVvbXZlIHRvbyBsYXJnZSBvciB0b28gc21hbGwgb2JqZWN0cyBiZWZvcmUgd2F0ZXJzaGVkIHRvIHJlZHVjZSBjb21wdXRhdGlvbmFsIGxvYWQKICAgIGlmICghd2F0ZXJzaGVkKSB7CiAgICAgICAgb2JqZWN0X3NoYXBlIDwtIGNvbXB1dGVGZWF0dXJlcy5zaGFwZShpbWFnZV9vYmplY3QpICU+JSBhc190aWJibGUocm93bmFtZXMgPSAiT2JqZWN0SUQiKQogICAgICAgIG9iamVjdF9zaGFwZV9yb3VuZCA8LSBvYmplY3Rfc2hhcGUgJT4lCiAgICAgICAgICAgICMgQXJlYQogICAgICAgICAgICBmaWx0ZXIocy5hcmVhID4gMzAwICYgcy5hcmVhIDwgMjAwMDApCiAgICB9CgogICAgIyBGaWx0ZXIgZm9yIGNpcmN1bGFyaXR5IG9ubHkgYWZ0ZXIgd2F0ZXJzaGVkIHNlZ21lbnRhdGlvbgogICAgaWYgKHdhdGVyc2hlZCkgewogICAgICAgIG9iamVjdF9zaGFwZSA8LSBjb21wdXRlRmVhdHVyZXMuc2hhcGUoaW1hZ2Vfb2JqZWN0KSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gIk9iamVjdElEIikKICAgICAgICBvYmplY3RfbW9tZW50IDwtIGNvbXB1dGVGZWF0dXJlcy5tb21lbnQoaW1hZ2Vfb2JqZWN0KSAlPiUgYXNfdGliYmxlKHJvd25hbWVzID0gIk9iamVjdElEIikKCiAgICAgICAgb2JqZWN0X3NoYXBlX3JvdW5kIDwtIG9iamVjdF9zaGFwZSAlPiUKICAgICAgICAgICAgbGVmdF9qb2luKG9iamVjdF9tb21lbnQsIGJ5ID0gIk9iamVjdElEIikgJT4lCiAgICAgICAgICAgICMgQ2lyY3VsYXJpdHkgPSAxIG1lYW5zIGEgcGVyZmVjdCBjaXJjbGUgYW5kIGdvZXMgZG93biB0byAwIGZvciBub24tY2lyY3VsYXIgc2hhcGVzCiAgICAgICAgICAgIG11dGF0ZShDaXJjdWxhcml0eSA9IDQgKiBwaSAqIHMuYXJlYSAvIHMucGVyaW1ldGVyXjIpICU+JQogICAgICAgICAgICBmaWx0ZXIoQ2lyY3VsYXJpdHkgPiAwLjMpICU+JQogICAgICAgICAgICAjIFJlbW92ZSB0YXBlIGFuZCBsYWJlbCB0aGF0IGhhcyByZWFsbHkgbGFyZ2UgdmFyaWF0aW9uIGluIHJhZGl1cwogICAgICAgICAgICBmaWx0ZXIocy5yYWRpdXMuc2Qvcy5yYWRpdXMubWVhbiA8IDEvMikgJT4lCiAgICAgICAgICAgIGZpbHRlcihtLmVjY2VudHJpY2l0eSA8IDAuOCkgIyBDaXJjbGUgZWNjZW50cmljaXR5PTAsIHN0cmFpZ2h0IGxpbmUgZWNjZW50cmljaXR5PTEKICAgIH0KCiAgICAjIEFycmFuZ2UgYnkgYXJlYSBzaXplCiAgICBvYmplY3Rfc2hhcGVfcm91bmQgPC0gb2JqZWN0X3NoYXBlX3JvdW5kICU+JSBhcnJhbmdlKGRlc2Mocy5hcmVhKSkKICAgIG9iamVjdF9JRF9ub25yb3VuZCA8LSBvYmplY3Rfc2hhcGUkT2JqZWN0SURbIShvYmplY3Rfc2hhcGUkT2JqZWN0SUQgJWluJSBvYmplY3Rfc2hhcGVfcm91bmQkT2JqZWN0SUQpXQogICAgcmV0dXJuKG9iamVjdF9JRF9ub25yb3VuZCkKfQpgYGAKCgpGaWx0ZXIgb2JqZWN0cyBieSBzaXplCgpgYGB7cn0KaW1hZ2Vfb2JqZWN0IDwtIGJ3bGFiZWwoaW1hZ2VfdGhyZXNob2xkZWQpCm9iamVjdF9JRF9ub25yb3VuZCA8LSBkZXRlY3Rfbm9ucm91bmRfb2JqZWN0KGltYWdlX29iamVjdCwgd2F0ZXJzaGVkID0gRikKaW1hZ2Vfcm91bmQgPC0gcm1PYmplY3RzKGltYWdlX29iamVjdCwgb2JqZWN0X0lEX25vbnJvdW5kLCByZWVudW1lcmF0ZSA9IFQpCndyaXRlSW1hZ2UoaW1hZ2Vfcm91bmQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wNC1zaXplX29iamVjdHMudGlmZiIpKQpkaXNwbGF5KGltYWdlX3JvdW5kLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKIyA1LiBEaXN0YW5jZSBtYXAKClRoZSBkaXN0YW5jZSBtYXAgY29udGFpbnMgZm9yIGVhY2ggcGl4ZWwgdGhlIGRpc3RhbmNlIHRvIHRoZSBuZWFyZXN0IGJhY2tncm91bmQgcGl4ZQoKCmBgYHtyfQppbWFnZV9kaXN0YW5jZW1hcCA8LSBkaXN0bWFwKGltYWdlX3JvdW5kKQp3cml0ZUltYWdlKG5vcm1hbGl6ZShpbWFnZV9kaXN0YW5jZW1hcCksIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wNS1kaXN0YW5jZW1hcC50aWZmIikpCmRpc3BsYXkobm9ybWFsaXplKGltYWdlX2Rpc3RhbmNlbWFwKSwgbWV0aG9kID0gInJhc3RlciIpCmBgYAoKCiMgNi4gV2F0ZXJzaGVkCgpUaGlzIGlzIHRoZSBhY3R1YWwgc2VnbWVudGF0aW9uIHN0ZXAuIEhlcmUgdGhlIGFkamFjZW50IG9iamVjdHMgYXJlIGJldHRlciBkaXN0aW5ndWlzaGVkCgpgYGB7cn0KaW1hZ2Vfd2F0ZXJzaGVkIDwtIHdhdGVyc2hlZChpbWFnZV9kaXN0YW5jZW1hcCwgdG9sZXJhbmNlID0gMSkKIyBBIHNlY29uZCBmaWx0ZXIgYnkgZGVuc2l0eSBhbmQgZWNjZW50cmljaXR5Cm9iamVjdF9JRF9ub25yb3VuZDIgPC0gZGV0ZWN0X25vbnJvdW5kX29iamVjdChpbWFnZV93YXRlcnNoZWQsIHdhdGVyc2hlZCA9IFQpCmltYWdlX3dhdGVyc2hlZDIgPC0gcm1PYmplY3RzKGltYWdlX3dhdGVyc2hlZCwgb2JqZWN0X0lEX25vbnJvdW5kMiwgcmVlbnVtZXJhdGUgPSBUKQp3cml0ZUltYWdlKGNvbG9yTGFiZWxzKGltYWdlX3dhdGVyc2hlZDIpLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDYtd2F0ZXJzaGVkLnRpZmYiKSkKZGlzcGxheShjb2xvckxhYmVscyhpbWFnZV93YXRlcnNoZWQyKSwgbWV0aG9kID0gInJhc3RlciIpCmBgYAojIDcuIEZlYXR1cmVzCgpgYGB7cn0KaW1hZ2Vfd2F0ZXJzaGVkIDwtIHdhdGVyc2hlZChpbWFnZV9kaXN0YW5jZW1hcCwgdG9sZXJhbmNlID0gMSkKZGlzcGxheShjb2xvckxhYmVscyhpbWFnZV93YXRlcnNoZWQpLCBtZXRob2QgPSAicmFzdGVyIikKYGBgCgoKIyMKCmBgYHtyfQojIDcuIENhbGN1bGF0ZSB0aGUgZmVhdHVyZXMKbGlicmFyeShFQkltYWdlRXh0cmEpCmxpYnJhcnkocHVycnIpCmV4dHJhY3RfdHJhbnNlY3Rpb24gPC0gZnVuY3Rpb24gKHdhdGVyc2hlZCwgcmVmKSB7CiAgICAjJyBUaGlzIGZ1bmN0aW9uIHNlYXJjaGVzIGZvciBhbGwgb2JqZWN0cyBvbiBhbiBpbWFnZSBhbmQgcmV0dXJuIHRoZSBwaXhlbCBpbnRlbnNpdHkgYWxvbmcgdGhlIHRyYW5zZWN0aW9uIG9mIGVhY2ggb2JqZWN0CiAgICAjJyBBcmd1bWVudHM6CiAgICAjJyAgd2F0ZXJzaGVkIGlzIHRoZSB3YXRlcnNoZWQgaW1hZ2Ugd2l0aCBvYmplY3QgbGFiZWxzCiAgICAjJyAgcmVmIGlzIHRoZSBvcmlnaW5hbCBpbWFnZSB3aXRoIGludGVuc2l0eSBkYXRhLiBUaGlzIGhhcyB0byBiZSBzaW5nbGUtY2hhbm5lbAogICAgb2MgPC0gb2NvbnRvdXIod2F0ZXJzaGVkKSAjIGFsbCBjb250b3VyIHBvaW50cwogICAgdHJhbnNlY3Rpb24gPC0gTlVMTAogICAgI21heF9yYWRpdXMgPC0gTlVMTCAjIEEgdGVzdCBmb3Igb2JqZWN0IElEIC0+IGdvb2QuIEFzIGxvbmcgSSB1c2UgdGhlIG9jIG91dHB1dCwgdGhleSBzaG91bGQgYmUgZ29vZAogICAgZm9yIChqIGluIDE6bGVuZ3RoKG9jKSkgewogICAgICAgIHogPC0gb2NbW2pdXSAjIGNvb3JkaW5hdGUgb2YgY29udG91ciBwaXhlbHMKICAgICAgICBjeiA8LSBhcHBseSh6LCAyLCBtZWFuKSAjIG9iamVjdCBjZW50ZXIKICAgICAgICByYWRpdXMgPC0gc3FydChyb3dTdW1zKCh6IC0gcmVwKGN6LCBlYWNoPW5yb3coeikpKV4yKSkgIyBhbGwgcmFkaXVzIGZyb20gdGhlIGNlbnRlciBwaXhlbCB0byB0aGUgY29udG91ciBwaXhlbHMKICAgICAgICBteiA8LSB6W3doaWNoLm1pbihhYnMocmFkaXVzIC0gbWVkaWFuKHJhZGl1cykpKSxdICMgdGhlIGNvb3JkaW5hdGUgb2YgY29udG91ciBwaXhlbHMgd2l0aCB0aGUgbWVkaWFuIHJhZGl1cyAob3IgY2xvc2VzdCB0byB0aGUgbWVkaWFuKQogICAgICAgICMgVGhlIGNvb3JkaW5hdGVzIG9mIGFsbCBhbG9uZyB0aGUgbGluZSB0byB0aGUgbWF4aW11bSBsaW5lOiB0aGlzIGlzIGFycmFuZ2VkIHN1Y2ggdGhhdCBpdCdzIGFsd2F5cyBmcm9tIHRoZSBjZW50ZXIgdG8gdGhlIHBlcmlwaGVyeQogICAgICAgIGx6IDwtIGJyZXNlbmhhbShjKGN6WzFdLCBtelsxXSksIGMoY3pbMl0sIG16WzJdKSkKICAgICAgICByYWRpYWxfZ3JhZGllbnQgPC0gTlVMTAogICAgICAgIGZvciAoayBpbiAxOmxlbmd0aChseiR4KSkgcmFkaWFsX2dyYWRpZW50W2tdIDwtIHJlZkAuRGF0YVtseiR4W2tdLCBseiR5W2tdXQoKICAgICAgICB0cmFuc2VjdGlvbltbal1dIDwtIHRpYmJsZSh4ID0gbHokeCwgeSA9IGx6JHksIEludGVuc2l0eSA9IHJhZGlhbF9ncmFkaWVudCkKICAgIH0KCiAgICByZXR1cm4odHJhbnNlY3Rpb24pCn0KZHJhd19waXhlbHMgPC0gZnVuY3Rpb24gKGltZywgcGl4ZWwueCwgcGl4ZWwueSkgewogICAgIycgVGhpcyBmdW5jdGlvbiB0YWtlcyBhbiBpbWFnZSBhbmQgZHJhdyByZWQgb24gdGhlIGFzc2lnbmVkIHBpeGVscwogICAgc3RvcGlmbm90KGxlbmd0aChwaXhlbC54KSA9PSBsZW5ndGgocGl4ZWwueSkpCgogICAgIyBSZW5kZXIgdGhlIGdyYXktc2NhbGUgaW1hZ2UgYmFjayBpbnRvIGNvbG9yIG1vZGUKICAgIGltZyA8LSBhYmluZChpbWcsIGltZywgaW1nLCBhbG9uZyA9IDMpICU+JSBJbWFnZShjb2xvcm1vZGUgPSAiQ29sb3IiKQoKICAgICMgQ3JlYXRlIGEgbG9naWNhbCBtYXNrCiAgICBtIDwtIEltYWdlKFQsIGRpbShpbWcpWzE6Ml0pCiAgICBmb3IgKGkgaW4gMTpsZW5ndGgocGl4ZWwueCkpIG1bcGl4ZWwueFtpXSwgcGl4ZWwueVtpXV0gPC0gRgogICAgTSA8LSBhYmluZChtLCBtLCBtLCBhbG9uZyA9IDMpCgogICAgIyBDb21iaW5lIHdpdGggc29saWQgY29sb3JlZCBpbWFnZSwgcmVwbGFjZSBhcHByb3ByaWF0ZSBwaXhlbHMgaW4gaW1hZ2UKICAgIG1hc2sgPC0gSW1hZ2UoInJlZCIsIGRpbSA9IGRpbShpbWcpWzE6Ml0sIGNvbG9ybW9kZSA9ICJDb2xvciIpCiAgICBhbnMgPC0gaW1nICogTSArIG1hc2sgKiAhTQoKICAgIHJldHVybihhbnMpCn0KYGBgCgoKCmBgYHtyfQppbWFnZV9uYW1lIDwtIGxpc3RfaW1hZ2VzJGltYWdlX25hbWVbaV0KaW1hZ2Vfcm9sbGVkIDwtIHJlYWRJbWFnZShwYXN0ZTAobGlzdF9pbWFnZXMkZm9sZGVyX2dyZWVuX3JvbGxlZFtpXSwgaW1hZ2VfbmFtZSwgIi50aWZmIikpCmxvYWQocGFzdGUwKGxpc3RfaW1hZ2VzJGZvbGRlcl9ncmVlbl93YXRlcnNoZWRfZmlsZVtpXSwgaW1hZ2VfbmFtZSwgIi5SRGF0YSIpKSAjIHRoaXMgc2hvdWxkIGNvbnRhaW4gb25lIFIgb2JqZWN0IGltYWdlX3dhdGVyc2hlZDIKCnRyYW5zZWN0aW9uIDwtIGV4dHJhY3RfdHJhbnNlY3Rpb24oaW1hZ2Vfd2F0ZXJzaGVkMiwgaW1hZ2Vfcm9sbGVkKSAlPiUKICAgIGxhcHBseShmdW5jdGlvbih4KSBtdXRhdGUoeCwgRGlzdGFuY2VUb0NlbnRlciA9IDE6bGVuZ3RoKHgpKSkgJT4lCiAgICBiaW5kX3Jvd3MoLmlkID0gIk9iamVjdElEIikKCiMgU2F2ZSB0aGUgdHJhbnNlY3Rpb24gaW1hZ2UKaW1hZ2VfdHJhbnNlY3QgPC0gZHJhd19waXhlbHMoaW1hZ2Vfcm9sbGVkLCB0cmFuc2VjdGlvbiR4LCB0cmFuc2VjdGlvbiR5KQp3cml0ZUltYWdlKGltYWdlX3RyYW5zZWN0LCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDctdHJhbnNlY3Rpb24udGlmZiIpKQpkaXNwbGF5KGltYWdlX3RyYW5zZWN0LCBtZXRob2QgPSAicmFzdGVyIikKCmBgYAoKClBsb3QgdHdvIHBhaXJzJ3MgdHJhbnNlY3Rpb24gZm9yIGV4YW1wbGUKCmBgYHtyfQoicmVhZCB0d28gdHJhbnNlY3Rpb24gZGF0YSAoaW4gdGFuc2VjdCBmb2xkZXIgYW5kIGNzdikgYW5kIHBsb3QgaXQgIgoKcmVhZF9jc3YocGFzdGUwKGZvbGRlcl9tYWluLCApKQoKIyB0cmFuc2VjdGlvbnMKIwojIGkgPSA1CiMgdHJhbnNlY3Rpb24gPC0gdHJhbnNlY3Rpb25zW1tpXV0KIwojICMgcGxvdCB0d28gaXNvbGF0ZXMKIwojIGJpbmRfcm93cyhtdXRhdGUodHJhbnNlY3Rpb25zW1s0XV0sIEdyb3VwID0gImlzb2xhdGUxIiksCiMgICAgICAgICAgIG11dGF0ZSh0cmFuc2VjdGlvbnNbWzVdXSwgR3JvdXAgPSAiaXNvbGF0ZTIiKSwKIyAgICAgICAgICAgbXV0YXRlKHRyYW5zZWN0aW9uc1tbNl1dLCBHcm91cCA9ICJwYWlyIikKIyApICU+JQojICAgICBnZ3Bsb3QoKSArCiMgICAgIGdlb21fbGluZShhZXMoeCA9IERpc3RhbmNlVG9DZW50ZXIsIHkgPSBJbnRlbnNpdHksIGdyb3VwID0gaW50ZXJhY3Rpb24oR3JvdXAsIE9iamVjdElEKSwgY29sb3IgPSBHcm91cCksIGx3ZCA9IC4zKSArCiMgICAgICNnZW9tX3BvaW50KGRhdGEgPSB0cmFuc2VjdGlvbnNbWzFdXSwgYWVzKHggPSBEaXN0YW5jZVRvQ2VudGVyLCB5ID0gSW50ZW5zaXR5LCBncm91cCA9IE9iamVjdElEKSkgKwojICAgICB0aGVtZV9jbGFzc2ljKCkgKwojICAgICAjZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSArCiMgICAgIGxhYnMoKQoKCgpgYGAKCgoKIyBPdXRwdXQgdGhlIGV4YW1wbGUKCmBgYHtyfQojICMgd3JpdGVJbWFnZShpbWFnZV9vcmlnaW5hbCwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTAwLW9yaWdpbmFsLnRpZmYiKSkKIyAjIHdyaXRlSW1hZ2UoaW1hZ2VfZ3JlZW4sIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMS1ncmVlbi50aWZmIikpCiMgd3JpdGVJbWFnZShpbWFnZV9yb2xsZWQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMi1yb2xsZWQudGlmZiIpKQojIHdyaXRlSW1hZ2UoaW1hZ2VfdGhyZXNob2xkZWQsIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wMy10aHJlc2hvbGQudGlmZiIpKQojICN3cml0ZUltYWdlKGNvbG9yTGFiZWxzKGltYWdlX29iamVjdCksIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wM2Etb2JqZWN0LnRpZmYiKSkKIyB3cml0ZUltYWdlKGltYWdlX3JvdW5kLCBwYXN0ZTAoZm9sZGVyX21haW4sICJleGFtcGxlLyIsIGltYWdlX25hbWUsICItMDQtcm91bmRfb2JqZWN0LnRpZmYiKSkKIyB3cml0ZUltYWdlKG5vcm1hbGl6ZShpbWFnZV9kaXN0YW5jZW1hcCksIHBhc3RlMChmb2xkZXJfbWFpbiwgImV4YW1wbGUvIiwgaW1hZ2VfbmFtZSwgIi0wNS1kaXN0YW5jZV9tYXAudGlmZiIpKQojIHdyaXRlSW1hZ2UoY29sb3JMYWJlbHMoaW1hZ2Vfd2F0ZXJzaGVkKSwgcGFzdGUwKGZvbGRlcl9tYWluLCAiZXhhbXBsZS8iLCBpbWFnZV9uYW1lLCAiLTA2LXdhdGVyc2hlZC50aWZmIikpCmBgYAoKCgoKCgoKCgoKCgo=